package pt.ipleiria.plp;

import pt.ipleiria.plp.Lixeiras.Lixeira;
import pt.ipleiria.plp.Lixeiras.LixeiraSendResult;
import pt.ipleiria.plp.Lixeiras.LixeiraEdited;
import pt.ipleiria.plp.Lixeiras.LixeiraNew;
import pt.ipleiria.plp.Lixeiras.LixeiraPeddingComplete;
import pt.ipleiria.plp.Lixeiras.LixeiraSent;
import de.enough.polish.io.RmsStorage;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;
import org.apache.commons.lang.RandomStringUtils;
import pt.ipleiria.plp.stub.BlackSpotState;

/**
 * The class the manges the Lixeiras
 * @author Filipe Felisberto e Ricardo Silva
 */
public class LixeirasManager {

    private String app;
    private Vector lixeirasEnviadas;
    private Vector lixeirasNovas;
    private RmsStorage storage;

    /**
     * Empty constructor, to be used when a app key hasn't been defined.
     * Loads a vector containing the Lixeiras still not sent, or if it's the
     * first run, creates a new vector
     */
    public LixeirasManager() {
        storage = new RmsStorage();
        app = null;
        try {

            this.lixeirasNovas = (Vector) this.storage.read("PorEnviar_temp");


        } catch (IOException ex) {
            this.lixeirasNovas = new Vector();
        }
        this.lixeirasEnviadas = new Vector();
    }

    /**
     * Loads a vector containing the Lixeiras still not sent and a vector containing
     * the sent Lixeiras, or if it can't find the files it will create new ones
     * @param app the app key
     */
    public LixeirasManager(String app) {
        storage = new RmsStorage();
        this.app = app;
        try {

            this.lixeirasNovas = (Vector) this.storage.read("PorEnviar_" + app);


        } catch (IOException ex) {
            this.lixeirasNovas = new Vector();
        }
        try {

            this.lixeirasEnviadas = (Vector) this.storage.read("Enviadas_" + app);


        } catch (IOException ex) {
            this.lixeirasEnviadas = new Vector();
        }

    }

    /**
     * Contructor to be used when defining a new application key
     * @param app the new applicaion key
     * @param lixeirasNotSent the vector with the lixeiras still to be sent
     */
    private LixeirasManager(String app, Vector lixeirasNotSent) {
        storage = new RmsStorage();
        this.app = app;

        try {

            this.lixeirasNovas = (Vector) this.storage.read("PorEnviar_" + app);


        } catch (IOException ex) {
            this.lixeirasNovas = lixeirasNotSent;
        }
        try {

            this.lixeirasEnviadas = (Vector) this.storage.read("Enviadas_" + app);


        } catch (IOException ex) {
            this.lixeirasEnviadas = new Vector();
        }


    }

    /**
     * Saves the data on disk
     * @throws IOException
     */
    public void save() throws IOException {
        if (app != null) {
            this.storage.save(this.lixeirasNovas, "PorEnviar_" + app);
            this.storage.save(this.lixeirasEnviadas, "Enviadas_" + app);
            this.storage.save(this.lixeirasEnviadas, "Editadas_" + app);
        } else {
            this.storage.save(this.lixeirasNovas, "PorEnviar_temp");
        }
    }

    /**
     * Retrieves all the events created by the user and stored on the Server,
     * there is no service for this kind of task so a simple hack was used.
     * @param lat the latitude component of the gps position
     * @param long the long component of the gps position
     */
    public void getAllLixeirasFromServer(float lat, float longe) {

        LixeiraSent[] lixs = LixeiraSent.getAllWithinDistance(1000000,
                lat, longe, app);
        for (int i = 0; i < lixs.length; i++) {
            appendOrUpdateLixeirasEnviadas(lixs[i]);
        }


    }

    /**
     * Assigns a new Application Key
     * @param newApp the new key
     * @return the new Manager
     * @throws IOException
     */
    public LixeirasManager assignAppKey(String newApp) throws IOException {

        if (this.app != null) {

            this.save();

            return new LixeirasManager(newApp);

        } else {
            storage.delete("PorEnviar_temp");
            return new LixeirasManager(newApp, lixeirasNovas);

        }

    }

    /**
     * Creates and adds a new lixeira to the toSend List
     * 
     * @param values necessessary to create a new Lixeira
     * @return true is build sucessfull
     */
    public boolean criarNovaLixeira(String title, double latitude, double longitude,
            pt.ipleiria.plp.stub.WasteVolume quantity, java.lang.String[] classification,
            int estimatedNumberVolunteers, String estimatedWasteVolume, String notes,
            java.lang.String[] tags, ImagePath[] imagePath) {



        LixeiraNew lixeira = new LixeiraNew(getValidTempGuid(), title, latitude,
                longitude, quantity, classification, estimatedNumberVolunteers,
                estimatedWasteVolume, notes, tags, BlackSpotState.Unconfirmed);

        if (lixeira == null) {
            return false;
        }

        if (imagePath.length > 0) {

            lixeira.setImages(imagePath);
        }

        lixeirasNovas.addElement(lixeira);

        return true;
    }

    /**
     * adds a new Lixeira to the sent lixeiras vector
     * @param lixeira the lixeira to be added
     */
    public void addLixeiraEnviada(LixeiraSent lixeira) {
        lixeirasEnviadas.addElement(lixeira);
    }

    /**
     * adds a new Lixeira to the not sent lixeiras vector
     * @param lixeira the lixeira to be added
     */
    public void addLixeiraPorEnviar(LixeiraPeddingComplete lixeira) {
        lixeirasNovas.addElement(lixeira);
    }

    /**
     * removes a Lixeira from the sent lixeiras vector
     * @param lixeira the lixeira to be removed
     */
    public boolean removeLixeiraEnviada(LixeiraSent lixeira) {
        return lixeirasEnviadas.removeElement(lixeira);
    }

    /**
     * removes a Lixeira from the not sent lixeiras vector
     * @param lixeira the lixeira to be removed
     */
    public boolean removeLixeiraPorEnviar(LixeiraPeddingComplete lixeira) {
        return lixeirasNovas.removeElement(lixeira);
    }

    /**
     * removes a Lixeira from the sent lixeiras vector
     * @param lixeira the lixeira guid to be removed
     */
    public LixeiraSent removeLixeiraEnviada(String guid) {
        LixeiraSent lix = searchLixeirasEnviadas(guid);

        if (lix == null) {
            return null;
        }

        lixeirasEnviadas.removeElement(lix);
        return lix;
    }

    /**
     * removes a Lixeira from the not sent lixeiras vector
     * @param lixeira the lixeira guid to be removed
     */
    public LixeiraPeddingComplete removeLixeiraPorEnviar(String guid) {

        LixeiraPeddingComplete lix = searchLixeirasPorEnviadar(guid);

        if (lix == null) {
            return null;
        }

        lixeirasNovas.removeElement(lix);
        return lix;
    }

    /**
     * searchs the vector with the sent lixeiras for a lixeira
     * @param guid the id of the lixeira to be searched
     * @return the lixeira
     */
    public LixeiraSent searchLixeirasEnviadas(String guid) {
        Enumeration elms = lixeirasEnviadas.elements();

        while (elms.hasMoreElements()) {
            LixeiraSent lix = (LixeiraSent) elms.nextElement();

            if (lix.getGuid().compareTo(guid) == 0) {
                return lix;
            }
        }
        return null;
    }

    /**
     * searchs the vector with the not sent lixeiras for a lixeira
     * @param guid the id of the lixeira to be searched
     * @return the lixeira
     */
    public LixeiraPeddingComplete searchLixeirasPorEnviadar(String guid) {
        Enumeration elms = lixeirasNovas.elements();

        while (elms.hasMoreElements()) {
            LixeiraPeddingComplete lix = (LixeiraPeddingComplete) elms.nextElement();

            if (lix.getGuid().compareTo(guid) == 0) {
                return lix;
            }
        }
        return null;
    }

    /**
     * @return a new random guid still not in use
     */
    public String getValidTempGuid() {
        //"12345678-1234-1234-1234-123456789012"
        /**
         * Esta fução é necessaria pois todo o programa roda à volta do guid
         * mesmo na parte por enviar então para tornar o valor mesmo unico foi
         * criada esta função.
         */
        StringBuilder str = new StringBuilder();
        do {
            char[] hexa = {'a','b','c','d','e','f'};
            str.append(RandomStringUtils.random(8,0,5, true, true,hexa));
            str.append("-");
            str.append(RandomStringUtils.random(4,0,5, true, true,hexa));
            str.append("-");
            str.append(RandomStringUtils.random(4,0,5, true, true,hexa));
            str.append("-");
            str.append(RandomStringUtils.random(4,0,5, true, true,hexa));
            str.append("-");
            str.append(RandomStringUtils.random(12,0,5, true, true,hexa));
        } while (searchLixeirasPorEnviadar(str.toString()) != null);

        return str.toString();

    }

    /**
     *
     * @return an enurmeration of the sent vector
     */
    public Enumeration getLixeirasEnviadas() {

//        if(lixeirasEnviadas.size() == 0){
//            return null;
//        }

        return lixeirasEnviadas.elements();

    }

    /**
     *
     * @return an enurmeration of the not sent vector
     */
    public Enumeration getLixeirasPorEnviar() {
//        if(lixeirasNovas.size() == 0){
//            return null;
//        }
        return lixeirasNovas.elements();

    }

    /**
     *
     * @param lix the Lixeria to be changed or added to the vector
     */
    public void appendOrUpdateLixeirasEnviadas(LixeiraSent lix) {

        removeLixeiraPorEnviar(lix.getGuid());

        LixeiraSent ls = searchLixeirasEnviadas(lix.getGuid());
        if (ls != null) {

            lixeirasEnviadas.removeElement(ls);
            lixeirasEnviadas.addElement(lix);

        } else {
            lixeirasEnviadas.addElement(lix);
        }

    }

    /**
     * Calls the method send on all lixeiras
     * @return the result of the failed sends
     */
    public LixeiraSendResult[] sendLixeiras() {

        Vector errors = new Vector();

        Enumeration lixs = getLixeirasPorEnviar();

        while (lixs.hasMoreElements()) {

            LixeiraPeddingComplete lix = (LixeiraPeddingComplete) lixs.nextElement();

            LixeiraSendResult lsr = lix.send(app);
            if (lsr.wasSuceded()) {
                appendOrUpdateLixeirasEnviadas(lsr.getLixeira());
            } else if (lsr.getLixeira() != null) {
                errors.addElement(lsr);
                appendOrUpdateLixeirasEnviadas(lsr.getLixeira());
            } else {
                errors.addElement(lsr);
            }

        }
        LixeiraSendResult ret[] = new LixeiraSendResult[errors.size()];
        for (int i = 0; i < errors.size(); i++) {
            ret[i] = ((LixeiraSendResult) errors.elementAt(i));
        }
        return ret;
    }

    /**
     * Calls the method send on a lixeira and manages the result
     * @return the result of the errors
     */
    public String sendLixeira(LixeiraPeddingComplete lix) {

        LixeiraSendResult lsr = lix.send(app);
        if (lsr.getLixeira() != null) {
            appendOrUpdateLixeirasEnviadas(lsr.getLixeira());
            removeLixeiraPorEnviar(lix);
        }
        return lsr.getError();
        
    }

    public String deleteLixeira(String guid, boolean enviada) {
        Lixeira lix;
        if (enviada) {
            lix=removeLixeiraEnviada(guid);
        } else {
            lix=removeLixeiraPorEnviar(guid);
        }

        return lix.delete(app);
    }

    /**
     * Saves the edit changes made to a Lixeira Sent in the phone
     * @param lix the lixeira to save
     * @return
     */
    public boolean editLixeiraSent(LixeiraEdited lix){

        if(removeLixeiraEnviada(lix.getGuid())==null){

            return false;

        }

        addLixeiraPorEnviar(lix);

        return true;
    }

}
